package com.piece.core.mongo.repository.impl;

import com.piece.core.framework.support.bootstrap.Order;
import com.piece.core.mongo.annotation.MongoQuery;
import com.piece.core.mongo.repository.IBaseRepository;
import com.piece.core.framework.support.bootstrap.DataTable;
import com.piece.core.framework.support.bootstrap.QueryTable;
import com.piece.core.framework.support.convert.Convert;
import com.piece.core.framework.util.string.StringUtil;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Sort;
import org.springframework.data.mongodb.core.MongoTemplate;
import org.springframework.data.mongodb.core.aggregation.Aggregation;
import org.springframework.data.mongodb.core.aggregation.AggregationOperation;
import org.springframework.data.mongodb.core.aggregation.GroupOperation;
import org.springframework.data.mongodb.core.query.Criteria;
import org.springframework.data.mongodb.core.query.Query;
import org.springframework.data.mongodb.core.query.Update;
import org.springframework.util.StringUtils;
import java.lang.reflect.Field;
import java.lang.reflect.ParameterizedType;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Map;

/**
 * mongo通用curd方法
 */
@Slf4j
public class BaseRepository<T> implements IBaseRepository<T> {

    @Autowired
    protected MongoTemplate mongoTemplate;

    // 获取需要操作的实体类class
    @SuppressWarnings("unchecked")
    protected Class<T> getEntityClass() {
        return ((Class<T>) ((ParameterizedType) getClass().getGenericSuperclass()).getActualTypeArguments()[0]);
    }

    /**
     * 保存一个对象到mongodb
     */
    @Override
    public T save(T bean) {
        mongoTemplate.save(bean);
        return bean;
    }

    /**
     * 保存一个对象到mongodb
     */
    @Override
    public T saveOrUpdate(String id, T t) {
        Query query = new Query();
        query.addCriteria(Criteria.where("_id").is(id));
        Update update = buildBaseUpdate(t);
        mongoTemplate.upsert(query, update, this.getEntityClass());
        return t;
    }

    /**
     * 根据id删除对象
     */
    @Override
    public void delete(T t) {
        mongoTemplate.remove(t);
    }

    /**
     * 删除对象
     */
    @Override
    public void deleteAll() {
        mongoTemplate.remove(new Query(), getEntityClass());
    }

    /**
     * 根据查询条件删除
     */
    @Override
    public void deleteByQuery(Query query) {
        mongoTemplate.remove(query, getEntityClass());
    }

    /**
     * 根据对象的属性删除
     */
    @Override
    public void deleteByCondition(T t) {
        Query query = buildBaseQuery(t);
        mongoTemplate.remove(query, getEntityClass());
    }

    /**
     * 通过条件查询更新数据
     */
    @Override
    public void update(Query query, Update update) {
        mongoTemplate.updateMulti(query, update, this.getEntityClass());
    }

    /**
     * 根据id进行更新
     */
    @Override
    public void updateById(String id, T t) {
        Query query = new Query();
        query.addCriteria(Criteria.where("_id").is(id));
        Update update = buildBaseUpdate(t);
        update(query, update);
    }

    /**
     * 通过条件查询实体(集合)
     */
    @Override
    public List<T> find(QueryTable queryTable) {
        Query query = new Query();
        Criteria criteria = executeCriteria(queryTable.getParams());
        if (null != criteria) {
            query.addCriteria(criteria);
        }
        executeSort(queryTable, query);
        return mongoTemplate.find(query, getEntityClass());
    }

    /**
     * 通过条件查询实体(集合)
     */
    @Override
    public List<T> findByCondition(T t) {
        Query query = buildBaseQuery(t);
        return mongoTemplate.find(query, getEntityClass());
    }

    /**
     * 通过条件查询实体(集合)
     */
    @Override
    public List<T> findByCondition(Map<String, Object> params) {
        Query query = executeQuery(params);
        return mongoTemplate.find(query, getEntityClass());
    }

    /**
     * 通过一定的条件查询一个实体
     */
    @Override
    public T findOne(Query query) {
        return mongoTemplate.findOne(query, getEntityClass());
    }

    /**
     * 通过ID获取记录
     */
    @Override
    public T get(String id) {
        return mongoTemplate.findById(id, getEntityClass());
    }

    /**
     * 通过ID获取记录,并且指定了集合名(表的意思)
     */
    @Override
    public T get(String id, String collectionName) {
        return mongoTemplate.findById(id, getEntityClass(), collectionName);
    }

    /**
     * 分页查询
     *
     * @param queryTable 查询条件,格式：Q_字段名称_类别_条件_关联关系<br>
     *                   Q: 固定查询标识<br>
     *                   类别:  S:string、 L:long、 DOU:double、 INT:int、 BD:BigDecimal、 FL:float、 SH:short、 D:date<br>
     *                   条件:  LT:<、 GT:>、 EQ:=、 LE:<=、 GE:>=、 LK:like、IN:in<br>
     *                   关联关系: AND、OR，默认为AND，暂时不可用
     */
    @Override
    public DataTable<T> findPage(QueryTable queryTable) {
        DataTable dataTable = new DataTable(queryTable);
        if (null == queryTable)
            return dataTable;
        List<T> result = new ArrayList<T>();
        try {
            Query query = new Query();
            Criteria criteria = executeCriteria(queryTable.getParams());
            if (null != criteria) {
                query.addCriteria(criteria);
            }
            long total = mongoTemplate.count(query, getEntityClass());
            dataTable.setRecordsTotal((int) total);
            if (null != queryTable.getLength() && queryTable.getLength() != 0) {
                query.skip(queryTable.getStart());
                query.limit(queryTable.getLength());
            }
            executeSort(queryTable, query);
            result = mongoTemplate.find(query, getEntityClass());
        } catch (Exception e) {
            log.error("Mongo分页查询异常：{}", e.getMessage());
        }
        dataTable.setData(result);
        return dataTable;
    }

    public long count(QueryTable queryTable) {
        try {
            Query query = new Query();
            Criteria criteria = executeCriteria(queryTable.getParams());
            if (null != criteria) {
                query.addCriteria(criteria);
            }
            return mongoTemplate.count(query, getEntityClass());
        } catch (Exception e) {
            log.error("Mongo查询总数异常：{}", e.getMessage());
        }
        return 0;
    }

    /**
     * 分组查询
     *
     * @param queryTable  查询条件,格式：Q_字段名称_类别_条件_关联关系<br>
     *                    Q: 固定查询标识<br>
     *                    类别:  S:string、 L:long、 DOU:double、 INT:int、 BD:BigDecimal、 FL:float、 SH:short、 D:date<br>
     *                    条件:  LT:<、 GT:>、 EQ:=、 LE:<=、 GE:>=、 LK:like、IN:in<br>
     *                    关联关系: AND、OR，默认为AND，暂时不可用
     * @param targetClass 分组结果类
     */
    @Override
    public List findByGroup(QueryTable queryTable, Class targetClass) {
        List result = new ArrayList<>();
        if (null == queryTable)
            return result;
        try {
            Criteria criteria = executeCriteria(queryTable.getParams());
            List<AggregationOperation> aggregationOperations = new ArrayList<>();
            Aggregation aggregation = null;
            GroupOperation groupOperation = null;
            String group = queryTable.getGroup();
            if (StringUtil.isNotEmpty(group)) {
                groupOperation = Aggregation.group(group).first(group).as(group).count().as("count");
                if (null != criteria) {
                    aggregationOperations.add(Aggregation.match(criteria));
                }
            }
            List<Order> orders = queryTable.getOrder();
            if (null != orders && orders.size() > 0) {
                for (Order order : orders) {
                    switch (order.getSort()) {
                        case ASC:
                            aggregationOperations.add(Aggregation.sort(Sort.by(Sort.Direction.ASC, order.getAttr())));
                        case DESC:
                            aggregationOperations.add(Aggregation.sort(Sort.by(Sort.Direction.DESC, order.getAttr())));
                    }
                }
            }
            if (null != groupOperation) {
                aggregationOperations.add(groupOperation);
            }
            aggregation = Aggregation.newAggregation(getEntityClass(), aggregationOperations);
            result = mongoTemplate.aggregate(aggregation, getEntityClass(), targetClass).getMappedResults();
        } catch (Exception e) {
            log.error("Mongo分组查询异常：{}", e.getMessage());
        }
        return result;
    }

    /**
     * 根据vo构建查询条件Query
     */
    protected Query buildBaseQuery(T t) {
        Query query = new Query();
        Field[] fields = t.getClass().getDeclaredFields();
        for (Field field : fields) {
            field.setAccessible(true);
            try {
                Object value = field.get(t);
                if (null != value) {
                    MongoQuery queryField = field.getAnnotation(MongoQuery.class);
                    if (null != queryField) {
                        query.addCriteria(queryField.type().buildCriteria(queryField, field, value));
                    }
                }
            } catch (Exception e) {
                log.error("创建Mongo查询条件异常：{}", e.getMessage());
            }
        }
        return query;
    }

    protected Update buildBaseUpdate(T t) {
        Update update = new Update();
        Field[] fields = t.getClass().getDeclaredFields();
        for (Field field : fields) {
            field.setAccessible(true);
            try {
                Object value = field.get(t);
                if (null != value) {
                    update.set(getQueryFieldName(field), value);
                }
            } catch (Exception e) {
                log.error("创建Mongo更新条件异常：{}", e.getMessage());
            }
        }
        return update;
    }

    protected String getQueryFieldName(Field field) {
        String queryFieldValue;
        MongoQuery queryField = field.getAnnotation(MongoQuery.class);
        if (null != queryField) {
            queryFieldValue = queryField.attribute();
            if (!StringUtils.hasText(queryFieldValue)) {
                queryFieldValue = field.getName();
            }
        } else {
            queryFieldValue = field.getName();
        }
        return queryFieldValue;
    }

    protected void executeSort(QueryTable queryTable, Query query) {
        List<Order> orders = queryTable.getOrder();
        if (!CollectionUtils.isEmpty(orders)) {
            List<Sort.Order> temp_orders = new ArrayList<>();
            for (Order order : orders) {
                switch (order.getSort()) {
                    case ASC:
                        temp_orders.add(new Sort.Order(Sort.Direction.ASC, order.getAttr()));
                    case DESC:
                        temp_orders.add(new Sort.Order(Sort.Direction.DESC, order.getAttr()));
                }
            }
            query.with(Sort.by(temp_orders));
        }
    }

    /**
     * 添加过滤的查询条件
     *
     * @param params {查询条件, 值},查询条件格式：Q_字段名称_类别_条件_关联关系<br>
     *               Q: 固定查询标识<br>
     *               类别:  S:string、 L:long、 DOU:double、 INT:int、 BD:BigDecimal、 FL:float、 SH:short、 D:date<br>
     *               条件:  LT:<、 GT:>、 EQ:=、 LE:<=、 GE:>=、 LK:like、IN:in<br>
     *               关联关系: AND、OR，默认为AND，暂时不可用
     */
    protected Query executeQuery(Map<String, Object> params) {
        Query query = new Query();
        if (null == params) {
            return query;
        }
        Criteria criteria = executeCriteria(params);
        query.addCriteria(criteria);
        return query;
    }

    /**
     * 添加过滤的查询条件
     *
     * @param params {查询条件, 值},查询条件格式：Q_字段名称_类别_条件_关联关系<br>
     *               Q: 固定查询标识<br>
     *               类别:  S:string、 L:long、 DOU:double、 INT:int、 BD:BigDecimal、 FL:float、 SH:short、 D:date<br>
     *               条件:  LT:<、 GT:>、 EQ:=、 LE:<=、 GE:>=、 LK:like、IN:in、NULL:exists=false、NNULL:exists=true<br>
     *               关联关系: AND、OR，默认为AND，暂时不可用
     */
    protected Criteria executeCriteria(Map<String, Object> params) {
        Criteria criteria = null;
        Criteria criteria1_temp = null;
        if (null == params) {
            return null;
        }
        Iterator<Map.Entry<String, Object>> it = params.entrySet().iterator();
        String operation, name, symbol;
        boolean hasRelation = false;
        Object value;
        List<Criteria> criterias = new ArrayList<>();
        while (it.hasNext()) {
            try {
                Map.Entry e = it.next();
                name = (String) e.getKey();
                if (!name.startsWith("Q_")) {
                    continue;
                }
                // OR条件查询
                hasRelation = (name.split("_").length == 5);
                if (hasRelation) {
                    symbol = name.split("_")[4];
                    // or条件单独放置
                    hasRelation = ("OR".equals(symbol));
                }
                symbol = name.split("_")[2];
                operation = name.split("_")[3];
                value = Convert.invert(symbol, e.getValue());
                name = name.split("_")[1];
                if ("LT".equals(operation)) {
                    criteria = Criteria.where(name).lt(value);
                } else if ("GT".equals(operation)) {
                    criteria = Criteria.where(name).gt(value);
                } else if ("LE".equals(operation)) {
                    criteria = Criteria.where(name).lte(value);
                } else if ("GE".equals(operation)) {
                    criteria = Criteria.where(name).gte(value);
                } else if ("LK".equals(operation)) {
                    criteria = Criteria.where(name).regex(value.toString());
                } else if ("IN".equals(operation)) {
                    criteria = Criteria.where(name).in(Convert.toStrArray(Convert.toStr(value)));
                } else if ("NULL".equals(operation)) {
                    criteria = Criteria.where(name).exists(false);
                } else if ("NNULL".equals(operation)) {
                    criteria = Criteria.where(name).exists(true);
                } else {
                    criteria = Criteria.where(name).is(value);
                }
                criterias.add(criteria);
            } catch (Exception e) {
                log.error("创建Mongo查询条件异常：{}", e.getMessage());
            }

            if (null == criteria1_temp) {
                criteria1_temp = criteria;
            } else {
                if (hasRelation) {
                    criteria1_temp = new Criteria().orOperator(criteria1_temp, criteria);
                } else {
                    criteria1_temp = new Criteria().andOperator(criteria1_temp, criteria);
                }
            }

        }
        return criteria1_temp;
    }
}
